﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.ComponentModel;

using Optimization.Utilities;

namespace Optimization.StandardSolvers
{
    [IsSolver]
    public class HSA : ParticleBasedMethod, ISolver
    {

        /* inherited
        public ClassFactory ProblemFactory = null;
        public IProblem Problem = null;

        protected Vector Solution;
        protected double SolutionValue;
        */

        //IProblem problem = null;
        double hmcr = 0.4;
        double par = 0.3;
        System.Random rnd = new Optimization.Utilities.Random();

        [CategoryAttribute("Algorithm Settings"), DescriptionAttribute("SwarmSize")]
        public int SwarmSize
        {
            get { return swarmSize; }
            set { swarmSize = value; InitSwarm(); }
        }

        [CategoryAttribute("HSA Settings"), DescriptionAttribute("Probability of choosing from particles")]
        public double HMCR
        {
            get { return hmcr; }
            set { hmcr = value; }
        }

        [CategoryAttribute("HSA Settings"), DescriptionAttribute("Probability of pitching (adapting new value)")]
        public double PAR
        {
            get { return par; }
            set { par = value; }
        }


        private void SortParticles()
        {
            this.Sort(delegate(Particle a, Particle b)
            {
                if (a.Value < b.Value)
                    return -1;
                if (a.Value > b.Value)
                    return 1;
                return 0;
            });
        }

        int swarmSize = 10;
        private void InitSwarm()
        {
            for (int i = 0; i < swarmSize; i++)
            {
                Vector solution = Problem.RandomSolution();
                double solutionValue = Problem.Evaluate(solution);

                Particle p = new Particle();
                p.Position = solution;
                p.Velocity = Vector.Zero(solution.Length);
                p.Value = solutionValue;

                this.Add(p);
            }

            Particle pBest = this[0];
            foreach (Particle cp in this)
                if (cp.Value < pBest.Value)
                    pBest = cp;

            this.SolutionValue = pBest.Value;
            this.Solution = pBest.Position.Copy();
        }

        public void DefineProblem(Func<IProblem> cf)
        {
            ProblemFactory = cf;
            Problem = ProblemFactory();

            InitSwarm();

        }

        public Vector GetSolution()
        {
            return Solution.Copy();
        }

        public void SetSolution(Vector solution, double solutionValue)
        {
            if (this.SolutionValue > solutionValue)
            {
                Particle maximum = this[0];
                foreach (Particle p in this)
                    if (p.Value > maximum.Value)
                        maximum = p;

                maximum.Position = solution.Copy();
                maximum.Value = solutionValue;
            }
        }

        public double GetSolutionValue()
        {
            return SolutionValue;
        }

        public void Step()
        {
            Vector NewSolution = Problem.RandomSolution();
            for (int i = 0; i < NewSolution.Length; i++)
            {

                if (rnd.NextDouble() < hmcr)
                {
                    //choose from memory
                    int index = rnd.Next(this.Count);
                    double NewXi = this[index].Position[i];
                    if (rnd.NextDouble() < par)
                    {
                        //Pitch adjustment

                        double rndU = (rnd.NextDouble() + rnd.NextDouble() + rnd.NextDouble() - 1.5) / 1.5;
                        NewXi = NewXi + rndU * (NewXi - NewSolution[i]);
                    }
                    else
                        NewSolution[i] = NewXi;
                }
                else
                {
                    //create new => keep random NewSolution[i]
                }
            }

            double NewSolutionValue = Problem.Evaluate(NewSolution);
            if (this[this.Count - 1].Value > NewSolutionValue)
            {
                this.RemoveAt(this.Count - 1);

                Particle p = new Particle();
                p.Position = NewSolution;
                p.Velocity = Vector.Zero(NewSolution.Length);
                p.Value = NewSolutionValue;
                this.Add(p);
            }

            SortParticles();

            Solution = this[0].Position.Copy();
            SolutionValue = this[0].Value;
        }
    }
}
